#version 330 core
out vec4 fragColor;

in vec3 normal;
in vec3 fragPos;
in vec2 texCoords;

struct Material {
    sampler2D textureDiffuse0;
    sampler2D textureDiffuse1;
    sampler2D textureDiffuse2;
    sampler2D textureSpecular3;
    sampler2D textureSpecular4;
    sampler2D emit;
    float shiness;
};

uniform Material material;

uniform float ambientFactor;
uniform float diffuseFactor;
uniform float specularFactor;
uniform float emitFactor;

struct DirLight {
    vec3 direction;
    vec3 color;
};

struct PointLight {
    vec3 position;
    vec3 color;

    float k0, k1, k2;
};

struct SpotLight {
    vec3 position;
    vec3 direction;
    vec3 color;

    float k0, k1, k2;
    float inner, outer;
};

#define POINT_LIGHT_NUM 1

uniform DirLight dirLight;
uniform PointLight pointLights[POINT_LIGHT_NUM];
uniform SpotLight spotLight;

uniform vec3 viewPos;

vec3 diffuseMaterial;
vec3 specularMaterial;
vec3 normalDir;
vec3 viewDir;
float shiness;

vec3 calc_DirLight(DirLight light);
vec3 calc_PointLight(PointLight light);
vec3 calc_SpotLight(SpotLight light);

float inverseDepth(float depth) {
    const float near = 0.1; const float far = 50;
    float z = depth * 2.0 - 1.0; // back to NDC
    return (2.0 * near * far) / (far + near - z * (far - near));
}

void main()
{
    diffuseMaterial = texture(material.textureDiffuse0, texCoords).rgb;
    specularMaterial = texture(material.textureSpecular3, texCoords).rgb;

//    fragColor = vec4(diffuseMaterial, 1.0f);

    normalDir = normalize(normal);
    viewDir = normalize(viewPos - fragPos);
    const float shinessFactor = 128.0f;
    shiness = material.shiness * shinessFactor;

    vec3 color = vec3(0.0f);

    color += calc_DirLight(dirLight);

    for (int i = 0; i < POINT_LIGHT_NUM; ++i) {
        color += calc_PointLight(pointLights[i]);
    }

    color += calc_SpotLight(spotLight);

    //vec3 emit = emitFactor * texture(material.emit, texCoords).rgb;

    fragColor = vec4(color, 1.0f);
}

vec3 calc_Bling_Phong(vec3 lightDir) {
    vec3 ambient = ambientFactor * diffuseMaterial;
    vec3 diffuse = diffuseFactor * diffuseMaterial * max(dot(lightDir, normalDir), 0);

    vec3 halfDir = normalize(viewDir + lightDir);
    vec3 specular = specularFactor * specularMaterial * pow(max(dot(halfDir, normalDir), 0), shiness);

    return ambient + diffuse + specular;
}

vec3 calc_DirLight(DirLight light) {
    vec3 lightDir = normalize(-light.direction);

    /// no attenuation
    return calc_Bling_Phong(lightDir) * light.color;
}

vec3 calc_PointLight(PointLight light) {
//    return vec3(0.0f);
    vec3 lightVec = light.position - fragPos;
    float dis = length(lightVec);
    float attenuation = 1.0f / (light.k0 + light.k1 * dis + light.k2 * dis * dis);
    vec3 lightDir = normalize(lightVec);

    return calc_Bling_Phong(lightDir) * light.color * attenuation;
}

vec3 calc_SpotLight(SpotLight light) {
//    return vec3(0.0f);
    vec3 lightVec = light.position - fragPos;
    float dis = length(lightVec);
    float attenuation = 1.0f / (light.k0 + light.k1 * dis + light.k2 * dis * dis);
    vec3 lightDir = normalize(lightVec);
    float intensity = clamp((dot(lightDir, normalize(-light.direction)) - light.outer) / (light.inner - light.outer), 0.0f, 1.0f);

    return calc_Bling_Phong(lightDir) * light.color * attenuation * intensity;
}
